
`include "defines.v"
`include "aluop_defines.v"

module mem(

    input wire            clk,    
    input wire            rst,
    	
    input wire            rd_w_ena_i,
    input wire [4:0]      rd_w_addr_i,
    input wire [`REG_BUS] rd_w_data_i,
 
    input wire [5:0]      aluop_i,
    input wire [`RAM_BUS] ram_addr_i,
    input wire [`REG_BUS] rs2_i,     //store rs2


    //wb_stage
    output reg            rd_w_ena_o,
    output reg [4:0]      rd_w_addr_o,
    output reg [`REG_BUS] rd_w_data_o, 
    
    //dram
    output reg [`RAM_BUS] ram_addr_o,
    output reg            ram_wr_ena_o,
    output reg            ram_rd_ena_o,
    output reg [`D_BUS]   ram_wmask_o,
    output reg [`D_BUS]   ram_wr_data_o,
    input      [`D_BUS]   ram_rd_data_i
    );
    
    wire load_inst;
    wire store_inst;
    assign load_inst = ((aluop_i == `ALUOP_LB) || (aluop_i == `ALUOP_LH) || (aluop_i == `ALUOP_LW) || (aluop_i == `ALUOP_LD)
            || (aluop_i == `ALUOP_LBU) || (aluop_i == `ALUOP_LHU) || (aluop_i == `ALUOP_LWU)) ? 1'b1 : 1'b0;
    assign store_inst = ((aluop_i == `ALUOP_SB) || (aluop_i == `ALUOP_SH) || (aluop_i == `ALUOP_SW) || (aluop_i == `ALUOP_SD)) ? 1'b1 : 1'b0;
    /*
    always @ (*) begin
        if(rst == `RST) begin
            rd_w_ena_o    =  1'b0;
            rd_w_addr_o   =  `REG_ADDR_0;
            rd_w_data_o   =  `ZERO_WORD;
            ram_rd_ena_o  =  1'b0;
            ram_wr_ena_o  =  1'b0;
            ram_addr_o    =  `ZERO_WORD;
            ram_wr_data_o =  `ZERO_WORD;
            ram_wmask_o   =  `ZERO_WORD; 
        end
        else begin
            if(load_inst == 1'b1) begin
                rd_w_ena_o    = rd_w_ena_i;
                rd_w_addr_o   = rd_w_addr_i;
                ram_rd_ena_o  = 1'b1;
                ram_wr_ena_o  = 1'b0;
                ram_wr_data_o = `ZERO_WORD;
                ram_wmask_o   = `ZERO_WORD; 
                ram_addr_o    = ram_addr_i;
                case(aluop_i)
                    `ALUOP_LB  : rd_w_data_o = {{ 56{ram_rd_data_i[7]} },ram_rd_data_i[7:0]};
                    `ALUOP_LH  : rd_w_data_o = {{ 48{ram_rd_data_i[15]} },ram_rd_data_i[15:0]};
                    `ALUOP_LW  : rd_w_data_o = {{ 32{ram_rd_data_i[31]} },ram_rd_data_i[31:0]};
                    `ALUOP_LD  : rd_w_data_o = ram_rd_data_i;
                    `ALUOP_LBU : rd_w_data_o = {56'b0,ram_rd_data_i[7:0]};
                    `ALUOP_LHU : rd_w_data_o = {48'b0,ram_rd_data_i[15:0]}; 
                    `ALUOP_LWU : rd_w_data_o = {32'b0,ram_rd_data_i[32:0]}; 
                    default    : rd_w_data_o = rd_w_data_i;
                endcase
            end
            else if(store_inst == 1'b1)begin
                rd_w_ena_o   = 1'b0;
                rd_w_addr_o  = `REG_ADDR_0;
                rd_w_data_o  = `ZERO_WORD;
                ram_rd_ena_o = 1'b0;
                ram_wr_ena_o = 1'b1;
                ram_addr_o   = ram_addr_i;
                case(aluop_i)
                    `ALUOP_SB : begin ram_wmask_o = 64'h0000_0000_0000_00FF; ram_wr_data_o = { 56'b0, rs2_i[7:0] };  end
                    `ALUOP_SH : begin ram_wmask_o = 64'h0000_0000_0000_FFFF; ram_wr_data_o = { 48'b0, rs2_i[15:0] }; end  
                    `ALUOP_SW : begin ram_wmask_o = 64'h0000_0000_FFFF_FFFF; ram_wr_data_o = { 32'b0, rs2_i[31:0] }; end
                    `ALUOP_SD : begin ram_wmask_o = 64'hFFFF_FFFF_FFFF_FFFF; ram_wr_data_o = rs2_i; end
                    default   : begin ram_wmask_o = `ZERO_WORD; ram_wr_data_o = `ZERO_WORD; end  
                endcase
            end
            else begin
                rd_w_ena_o    = rd_w_ena_i;
                rd_w_addr_o   = rd_w_addr_i;
                rd_w_data_o   = rd_w_data_i;
                ram_rd_ena_o  = 1'b0;
                ram_wr_ena_o  = 1'b0;
                ram_wmask_o   = `ZERO_WORD;
                ram_addr_o    = `ZERO_WORD;
                ram_wr_data_o = `ZERO_WORD;
            end
        end
    end
    */
    //为了适应仿真，这里加入数据对齐，除了difftest外，其他测试不需要该处理
        always @ (*) begin
        if(rst == `RST) begin
            rd_w_ena_o    =  1'b0;
            rd_w_addr_o   =  `REG_ADDR_0;
            rd_w_data_o   =  `ZERO_WORD;
            ram_rd_ena_o  =  1'b0;
            ram_wr_ena_o  =  1'b0;
            ram_addr_o    =  `ZERO_WORD;
            ram_wr_data_o =  `ZERO_WORD;
            ram_wmask_o   =  `ZERO_WORD; 
        end
        else begin
            if(load_inst == 1'b1) begin
                rd_w_ena_o    = rd_w_ena_i;
                rd_w_addr_o   = rd_w_addr_i;
                ram_rd_ena_o  = 1'b1;
                ram_wr_ena_o  = 1'b0;
                ram_wr_data_o = `ZERO_WORD;
                ram_wmask_o   = `ZERO_WORD; 
                ram_addr_o    = ram_addr_i;
                case(aluop_i)
                    `ALUOP_LB  : 
                        begin
                            case(ram_addr_i[2:0])
                                3'd0:rd_w_data_o = {{56{ram_rd_data_i[7]}},ram_rd_data_i[ 7: 0]};
                                3'd1:rd_w_data_o = {{56{ram_rd_data_i[15]}},ram_rd_data_i[15: 8]};
                                3'd2:rd_w_data_o = {{56{ram_rd_data_i[23]}},ram_rd_data_i[23:16]};
                                3'd3:rd_w_data_o = {{56{ram_rd_data_i[31]}},ram_rd_data_i[31:24]};
                                3'd4:rd_w_data_o = {{56{ram_rd_data_i[39]}},ram_rd_data_i[39:32]};
                                3'd5:rd_w_data_o = {{56{ram_rd_data_i[47]}},ram_rd_data_i[47:40]};
                                3'd6:rd_w_data_o = {{56{ram_rd_data_i[55]}},ram_rd_data_i[55:48]};
                                3'd7:rd_w_data_o = {{56{ram_rd_data_i[63]}},ram_rd_data_i[63:56]};
                            endcase
                        end
                    `ALUOP_LH  : 
                        begin
                            case(ram_addr_i[2:1])
                                2'd0:rd_w_data_o = {{48{ram_rd_data_i[15]}},ram_rd_data_i[15: 0]};
                                2'd1:rd_w_data_o = {{48{ram_rd_data_i[31]}},ram_rd_data_i[31:16]};
                                2'd2:rd_w_data_o = {{48{ram_rd_data_i[47]}},ram_rd_data_i[47:32]};
                                2'd3:rd_w_data_o = {{48{ram_rd_data_i[63]}},ram_rd_data_i[63:48]};
                            endcase    
                        end
                    `ALUOP_LW  : 
                        begin
                            case(ram_addr_i[2])
                                1'b0:rd_w_data_o = {{48{ram_rd_data_i[31]}},ram_rd_data_i[31: 0]};
                                1'b1:rd_w_data_o = {{48{ram_rd_data_i[63]}},ram_rd_data_i[63:32]};
                            endcase
                        end
                    `ALUOP_LD  : rd_w_data_o = ram_rd_data_i;
                    `ALUOP_LBU : 
                        begin
                            case(ram_addr_i[2:0])
                                3'd0:rd_w_data_o = {56'd0,ram_rd_data_i[ 7: 0]};
                                3'd1:rd_w_data_o = {56'd0,ram_rd_data_i[15: 8]};
                                3'd2:rd_w_data_o = {56'd0,ram_rd_data_i[23:16]};
                                3'd3:rd_w_data_o = {56'd0,ram_rd_data_i[31:24]};
                                3'd4:rd_w_data_o = {56'd0,ram_rd_data_i[39:32]};
                                3'd5:rd_w_data_o = {56'd0,ram_rd_data_i[47:40]};
                                3'd6:rd_w_data_o = {56'd0,ram_rd_data_i[55:48]};
                                3'd7:rd_w_data_o = {56'd0,ram_rd_data_i[63:56]};
                            endcase
                        end
                    `ALUOP_LHU : 
                        begin
                            case(ram_addr_i[2:1])
                                2'd0:rd_w_data_o = {48'd0,ram_rd_data_i[15: 0]};
                                2'd1:rd_w_data_o = {48'd0,ram_rd_data_i[31:16]};
                                2'd2:rd_w_data_o = {48'd0,ram_rd_data_i[47:32]};
                                2'd3:rd_w_data_o = {48'd0,ram_rd_data_i[63:48]}; 
                            endcase   
                        end
                    `ALUOP_LWU : 
                        begin
                            case(ram_addr_i[2])
                                1'b0:rd_w_data_o = {32'd0,ram_rd_data_i[31: 0]};
                                1'b1:rd_w_data_o = {32'd0,ram_rd_data_i[63:32]};
                            endcase
                        end
                    default    : rd_w_data_o = rd_w_data_i;
                endcase
            end
            else if(store_inst == 1'b1)begin
                rd_w_ena_o   = 1'b0;
                rd_w_addr_o  = `REG_ADDR_0;
                rd_w_data_o  = `ZERO_WORD;
                ram_rd_ena_o = 1'b0;
                ram_wr_ena_o = 1'b1;
                ram_addr_o   = ram_addr_i;
                case(aluop_i)
                    `ALUOP_SB : 
                        begin
                            case(ram_addr_i[2:0])
                                3'd0:begin 
                                    ram_wmask_o   = 64'h0000_0000_0000_00FF;
                                    ram_wr_data_o = {56'd0,rs2_i[7:0]};
                                end
                                3'd1:begin 
                                    ram_wmask_o   = 64'h0000_0000_0000_FF00;
                                    ram_wr_data_o = {48'd0,rs2_i[7:0],8'd0};
                                end
                                3'd2:begin 
                                    ram_wmask_o   = 64'h0000_0000_00FF_0000;
                                    ram_wr_data_o = {40'd0,rs2_i[7:0],16'd0};
                                end
                                3'd3:begin 
                                    ram_wmask_o   = 64'h0000_0000_FF00_0000;
                                    ram_wr_data_o = {32'd0,rs2_i[7:0],24'd0};
                                end
                                3'd4:begin 
                                    ram_wmask_o   = 64'h0000_00FF_0000_0000;
                                    ram_wr_data_o = {24'd0,rs2_i[7:0],32'd0};
                                end
                                3'd5:begin 
                                    ram_wmask_o   = 64'h0000_FF00_0000_0000;
                                    ram_wr_data_o = {16'd0,rs2_i[7:0],40'd0};
                                end
                                3'd6:begin 
                                    ram_wmask_o   = 64'h00FF_0000_0000_0000;
                                    ram_wr_data_o = {8'd0,rs2_i[7:0],48'd0};
                                end
                                3'd7:begin 
                                    ram_wmask_o   = 64'hFF00_0000_0000_0000;
                                    ram_wr_data_o = {rs2_i[7:0],56'd0};
                                end
                            endcase
                        end
                    `ALUOP_SH : 
                        begin
                            case(ram_addr_i[2:1])
                                2'd0:begin  
                                    ram_wmask_o   = 64'h0000_0000_0000_FFFF;
                                    ram_wr_data_o = {48'd0,rs2_i[15:0]};
                                end
                                2'd1:begin  
                                    ram_wmask_o   = 64'h0000_0000_FFFF_0000;
                                    ram_wr_data_o = {32'd0,rs2_i[15:0],16'd0};
                                end
                                2'd2:begin  
                                    ram_wmask_o   = 64'h0000_FFFF_0000_0000;
                                    ram_wr_data_o = {16'd0,rs2_i[15:0],32'd0};
                                end
                                2'd3:begin  
                                    ram_wmask_o   = 64'hFFFF_0000_0000_0000;
                                    ram_wr_data_o = {rs2_i[15:0],48'd0};
                                end
                            endcase
                        end  
                    `ALUOP_SW : 
                        begin
                            case(ram_addr_i[2])
                                1'b0:begin  
                                    ram_wmask_o   = 64'h0000_0000_FFFF_FFFF;
                                    ram_wr_data_o = {32'd0,rs2_i[31:0]};
                                end
                                1'b1:begin  
                                    ram_wmask_o   = 64'hFFFF_FFFF_0000_0000;
                                    ram_wr_data_o = {rs2_i[31:0],32'd0};
                                end
                            endcase
                        end
                    `ALUOP_SD : begin ram_wmask_o = 64'hFFFF_FFFF_FFFF_FFFF; ram_wr_data_o = rs2_i; end
                    default   : begin ram_wmask_o = `ZERO_WORD; ram_wr_data_o = `ZERO_WORD; end  
                endcase
            end
            else begin
                rd_w_ena_o    = rd_w_ena_i;
                rd_w_addr_o   = rd_w_addr_i;
                rd_w_data_o   = rd_w_data_i;
                ram_rd_ena_o  = 1'b0;
                ram_wr_ena_o  = 1'b0;
                ram_wmask_o   = `ZERO_WORD;
                ram_addr_o    = `ZERO_WORD;
                ram_wr_data_o = `ZERO_WORD;
            end
        end
    end
endmodule
